/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.util.evaluators.impl;

import com.hercules.android.receiver.AndroidReceiver;
import com.hercules.android.receiver.ApplicationData;
import com.hercules.android.receiver.LogUtil;
import com.hercules.android.receiver.model.objects.XREObject;
import com.hercules.android.receiver.util.evaluators.InvalidAndroidReceiverStateException;

import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <p>
 * Helper class for the component. It provides useful common methods for all the classes in this component.
 * </p>
 *
 * <p>
 * <strong>Thread Safety: </strong> This class has no state, and thus it is thread safe.
 * </p>
 *
 * @author sparemax
 * @version 1.0
 */
final class Helper {
    /**
     * <p>
     * Represents the '.'.
     * </p>
     */
    static final String DOT = ".";

    /**
     * <p>
     * Represents the entrance message.
     * </p>
     *
     * <p>
     * Arguments:
     * <ol>
     * <li>the method signature.</li>
     * </ol>
     * </p>
     */
    private static final String MESSAGE_ENTRANCE = "Entering method [%1$s].";

    /**
     * <p>
     * Represents the exit message.
     * </p>
     *
     * <p>
     * Arguments:
     * <ol>
     * <li>the method signature.</li>
     * <li>the call duration time.</li>
     * </ol>
     * </p>
     */
    private static final String MESSAGE_EXIT = "Exiting method [%1$s], time spent in the method: %2$s milliseconds.";

    /**
     * <p>
     * Represents the error message.
     * </p>
     *
     * <p>
     * Arguments:
     * <ol>
     * <li>the method signature.</li>
     * <li>the error message.</li>
     * </ol>
     * </p>
     */
    private static final String MESSAGE_ERROR = "Error in method [%1$s], details: %2$s";

    /**
     * <p>
     * Prevents to create a new instance.
     * </p>
     */
    private Helper() {
        // empty
    }

    /**
     * <p>
     * Validates the value of an integer. The value should be positive.
     * </p>
     *
     * @param value
     *            the value of the variable to be validated.
     * @param name
     *            the name of the variable to be validated.
     *
     * @throws IllegalArgumentException
     *             if value is not positive.
     */
    static void checkPositive(int value, String name) {
        if (value <= 0) {
            throw new IllegalArgumentException("'" + name + "' should be positive.");
        }
    }

    /**
     * <p>
     * Validates the value of a variable. The value can not be <code>null</code>.
     * </p>
     *
     * @param value
     *            the value of the variable to be validated.
     * @param name
     *            the name of the variable to be validated.
     *
     * @throws IllegalArgumentException
     *             if the value of the variable is <code>null</code>.
     */
    static void checkNull(Object value, String name) {
        if (value == null) {
            throw new IllegalArgumentException("'" + name + "' should not be null.");
        }
    }

    /**
     * <p>
     * Validates the value of a string. The value can not be <code>null</code> or an empty string.
     * </p>
     *
     * @param value
     *            the value of the variable to be validated.
     * @param name
     *            the name of the variable to be validated.
     *
     * @throws IllegalArgumentException
     *             if the given string is <code>null</code> or an empty string.
     */
    static void checkNullOrEmpty(String value, String name) {
        checkNull(value, name);

        checkEmpty(value, name);
    }

    /**
     * <p>
     * Gets a logger by name.
     * </p>
     *
     * @param loggerName
     *            the logger name.
     *
     * @return the logger or <code>null</code> if the loggerName is <code>null</code>.
     *
     * @throws IllegalArgumentException
     *             if loggerName is empty.
     */
    static Logger getLogger(String loggerName) {
        if (loggerName == null) {
            return null;
        }

        // Check empty
        checkEmpty(loggerName, "loggerName");

        return LogUtil.getLogger(loggerName);
    }

    /**
     * <p>
     * Gets the mapping from ID to XRE object.
     * </p>
     *
     * @param receiver
     *            the receiver.
     *
     * @return the mapping from ID to XRE object.
     *
     * @throws InvalidAndroidReceiverStateException
     *             if the provided receiver was not properly initialized.
     */
    static Map<Integer, XREObject> getElementsXREIdToObject(AndroidReceiver receiver)
        throws InvalidAndroidReceiverStateException {
        // Get application data from the receiver
        ApplicationData applicationData = receiver.getApplicationData();
        Helper.checkState(applicationData == null, "'receiver.getApplicationData()' cannot be null.");
        // Get mapping from ID to XRE object
        Map<Integer, XREObject> elementsXREIdToObject = applicationData.getElementsXREIdToObject();
        Helper.checkState(elementsXREIdToObject == null,
            "'receiver.getApplicationData().getElementsXREIdToObject()' cannot be null.");

        return elementsXREIdToObject;
    }

    /**
     * <p>
     * Checks state of object.
     * </p>
     *
     * @param isInvalid
     *            the state of object.
     * @param message
     *            the error message.
     *
     * @throws InvalidAndroidReceiverStateException
     *             if isInvalid is <code>true</code>.
     */
    static void checkState(boolean isInvalid, String message) throws InvalidAndroidReceiverStateException {
        if (isInvalid) {
            throw new InvalidAndroidReceiverStateException(message);
        }
    }

    /**
     * <p>
     * Logs for entrance into public methods at <code>INFO</code> level.
     * </p>
     *
     * <p>
     * <em>NOTE:</em> Logging is NOT performed if the logger is <code>null</code>.
     * </p>
     *
     *
     * @param logger
     *            the logger object.
     * @param signature
     *            the signature of the method to be logged.
     * @param paramNames
     *            the names of parameters to log .
     * @param params
     *            the values of parameters to log.
     */
    static void logEntrance(Logger logger, String signature, String[] paramNames, Object[] params) {
        if (logger == null) {
            // No logging
            return;
        }

        // Do logging
        logger.info(String.format(MESSAGE_ENTRANCE, signature));

        if (paramNames != null && params != null) {
            // Log parameters
            logParameters(logger, paramNames, params);
        }
    }

    /**
     * <p>
     * Logs for exit from public methods at <code>INFO</code> level.
     * </p>
     *
     * <p>
     * <em>NOTE:</em> Logging is NOT performed if the logger is <code>null</code>.
     * </p>
     *
     *
     * @param logger
     *            the logger object.
     * @param signature
     *            the signature of the method to be logged.
     * @param value
     *            the return value to log.
     * @param timestamp
     *            the timestamp while entering the method.
     */
    @SuppressWarnings("boxing")
    static void logExit(Logger logger, String signature, Object[] value, Date timestamp) {
        if (logger == null) {
            // No logging
            return;
        }

        // Do logging
        logger.info(String.format(MESSAGE_EXIT, signature, System.currentTimeMillis() - timestamp.getTime()));

        if (value != null) {
            // Log return value
            logger.info("The return value: " + String.valueOf(value[0]));
        }
    }

    /**
     * <p>
     * Logs the given exception and message at <code>SEVERE</code> level.
     * </p>
     *
     * <p>
     * <em>NOTE:</em> Logging is NOT performed if the logger is <code>null</code>.
     * </p>
     *
     * @param <T>
     *            the exception type.
     * @param logger
     *            The logger object.
     * @param signature
     *            the signature of the method to log.
     * @param exception
     *            the exception to log.
     * @param message
     *            the message to log.
     *
     * @return the passed in exception.
     */
    static <T extends Throwable> T logException(Logger logger, String signature, T exception, String message) {
        if (logger == null) {
            // No logging
            return exception;
        }

        String errorMessage = String.format(MESSAGE_ERROR, new Object[] {signature, message});

        // Do logging
        logger.log(Level.SEVERE, errorMessage, exception);

        return exception;
    }



    /**
     * <p>
     * Converts the AndroidReceiver object to a string.
     * </p>
     *
     * @param obj
     *            the AndroidReceiver object (not <code>null</code>).
     *
     * @return the converted string.
     */
    private static String toString(AndroidReceiver obj) {
        StringBuffer sb = new StringBuffer();

        ApplicationData applicationData = obj.getApplicationData();

        sb.append(obj.getClass().getName()).append(" {")
            // applicationData
            .append("applicationData=").append(toString(applicationData)).append("}");

        return sb.toString();
    }

    /**
     * <p>
     * Converts the ApplicationData object to a string.
     * </p>
     *
     * @param obj
     *            the ApplicationData object.
     *
     * @return the converted string.
     */
    private static String toString(ApplicationData obj) {
        if (obj == null) {
            return String.valueOf(obj);
        }

        StringBuffer sb = new StringBuffer();
        sb.append(obj.getClass().getName()).append(" {")
            // elementsXREIdToObject
            .append("elementsXREIdToObject=").append(obj.getElementsXREIdToObject()).append("}");

        return sb.toString();
    }

    /**
     * <p>
     * Logs the parameters at <code>INFO</code> level.
     * </p>
     *
     * @param logger
     *            the logger object (not <code>null</code>).
     * @param paramNames
     *            the names of parameters to log (not <code>null</code>).
     * @param params
     *            the values of parameters to log (not <code>null</code>).
     */
    private static void logParameters(Logger logger, String[] paramNames, Object[] params) {
        StringBuilder sb = new StringBuilder("The parameters: {");

        for (int i = 0; i < params.length; i++) {

            if (i > 0) {
                // Append a comma
                sb.append(", ");
            }

            Object param = params[i];

            sb.append(paramNames[i]).append("=").append(
                param instanceof AndroidReceiver ? toString((AndroidReceiver) param) : param);
        }
        sb.append("}.");

        // Do logging
        logger.info(sb.toString());
    }

    /**
     * <p>
     * Validates the value of a string. The value can not an empty string.
     * </p>
     *
     * @param value
     *            the value of the variable to be validated. (not <code>null</code>)
     * @param name
     *            the name of the variable to be validated.
     *
     * @throws IllegalArgumentException
     *             if the given string is an empty string.
     */
    private static void checkEmpty(String value, String name) {
        if (value.trim().length() == 0) {
            throw new IllegalArgumentException("'" + name + "' should not be an empty string.");
        }
    }
}
